<!--
 * @Descripttion: 课堂/课程分析详情-知识图谱组件
 * @version: V1.0.0.1
 * @Author: zhaozhenzhuo
 * @Date: 2025-10-23 14:51:33
 * @LastEditors: zhaozhenzhuo
 * @LastEditTime: 2025-11-07 10:59:32
-->
<template>
	<div class="knowledge-graph-wrapper" :class="{ 'course-graph': type === 'course' }">
		<div class="page-tabs-wrapper">
			<span
				class="tab-item"
				:class="{ active: activeTab === 'tree' }"
				@click="onTabItemClickHandle('tree')"
			>
				树图
			</span>
			<span
				class="tab-item"
				:class="{ active: activeTab === 'network' }"
				@click="onTabItemClickHandle('network')"
			>
				网图
			</span>
		</div>
		<div class="graph-search-wrapper" :class="{ 'course-graph-search': type === 'course' }">
			<div class="self-search-wrapper">
				<el-input
					class="self-radius"
					v-model="searchNodeValue"
					placeholder="输入图谱节点名称搜索"
					@keydown="onSearchNodeHandle"
				/>
				<img
					src="@/assets/images/search-icon.png"
					class="search-icon"
					alt=""
					@click="onSearchNodeHandle"
				/>
			</div>
			<!-- 搜索结果弹框 -->
			<div v-if="showSearchResults" class="search-results-popup">
				<div class="search-results-close" @click="closeSearchResults">
					<el-icon><Close /></el-icon>
				</div>
				<div class="search-results-title">检索到{{ searchResults.length }}个结果</div>
				<div v-if="searchResults.length" class="search-results-list">
					<div
						v-for="item in searchResults"
						:key="item.id"
						class="search-result-item"
						@click="onSearchResultItemClickHandle(item)"
					>
						<span class="search-result-tag" :class="item.tagClass">{{ item.tag }}</span>
						<div class="search-result-title">{{ item.title }}</div>
					</div>
				</div>
			</div>
		</div>
		<div class="page-graph-wrapper">
			<NetworkGraph
				v-if="activeTab === 'network'"
				ref="networkGraphRef"
				:initial-data="networkGraphData"
				:relationStyleByType="relationStyleByType"
				:nodeStyleByType="nodeStyleByType"
				:centerPosition="type === 'course' ? undefined : [0.3, 0.5]"
				:excludeNodeList="['root']"
				:typeMap="typeMap"
				:zoomValue="100"
				@node-click="handleNetworkNodeClick"
				@update:zoomValue="zoomValue = $event"
			></NetworkGraph>
			<TreeGraph
				v-else-if="activeTab === 'tree' && treeData"
				ref="treeGraphRef"
				:treeData="treeData"
				:themeConfig="themeConfig"
				:excludeNodeList="['root']"
				:typeMap="typeMap"
				:zoomValue="100"
				@node-click="handleTreeNodeClick"
				@update:zoomValue="zoomValue = $event"
			></TreeGraph>
		</div>

		<!-- 节点详情弹框 -->
		<NodeInfoDialog
			:is-visible-node-info-dialog="isVisibleNodeInfoDialog"
			type-name="知识点"
			:text-name="textName"
			type="lesson"
			:module-show="{
				nodeTitleShow: true,
				lessonInfoBlockShow: false,
				moduleTitleShow: false,
				knowledgePointBlockShow: false,
				teachingAccuracyBlockShow: false,
				teacherLectureBlockShow: false,
				lessonSectionBlockShow: false,
				teacherBehaviorBlockShow: false,
				teacherLanguageBlockShow: false,
				teacherQuestionBlockShow: false,
				teacherStudentInteractionBlockShow: false,
				relationBlockShow: true,
				videoBlockShow: true,
				lessonAnalysisBlockShow: false,
				footerShow: false
			}"
			:data="{
				knowledgeDetail,
				knowledgeRelation
			}"
			@closeNodeInfoDialog="closeNodeInfoDialog"
			@changeVideoTime="onChangeVideoTime"
		></NodeInfoDialog>

		<!-- 知识点关系筛选框 -->
		<div
			v-if="activeTab === 'network'"
			class="type-group-wrapper"
			:class="{ 'lesson-graph-type-group': type === 'lesson' }"
		>
			<div class="type-group-item-wrapper">
				<div class="type-group-item">
					<div class="type-group-title" @click="onTypeAllClickHandle">
						知识关系（{{ links.length }}）
					</div>
					<div class="type-sub-list">
						<span
							v-for="item in Array.from(linkMap.keys())"
							:key="item"
							class="type-sub-item"
							@click="onTypeSubItemClickHandle(relationMap.get(item)?.relationName || '')"
						>
							<span class="type-sub-item-icon"></span>
							<span class="type-sub-item-text"
								>{{ relationMap.get(item)?.relationName || '-' }}（{{
									linkMap.get(item)?.length || 0
								}}）</span
							>
						</span>
					</div>
				</div>
			</div>
			<div class="type-group-footer">
				<button class="type-group-btn" @click="onEnterManagementClickHandle">
					进入知识关系管理
				</button>
			</div>
		</div>

		<!-- 图谱工具栏 -->
		<div class="toolbar-wrapper" :class="{ 'lesson-graph-toolbar': type === 'lesson' }">
			<div class="grid-btn">
				<el-button
					><el-icon><Grid /></el-icon
				></el-button>
				<div class="grid-item-wrapper">
					<div class="grid-item" @click="onExpandClickHandle('all')">展开全部</div>
					<div class="grid-item" @click="onExpandClickHandle('1')">展开一级</div>
					<div class="grid-item" @click="onExpandClickHandle('2')">展开二级</div>
					<div class="grid-item" @click="onExpandClickHandle('3')">展开三级</div>
					<div class="grid-item" @click="onExpandClickHandle('4')">展开四级</div>
				</div>
			</div>
			<div class="center-btn" title="定位到中心" @click="onCenterClickHandle">
				<el-button
					><el-icon><Aim /></el-icon
				></el-button>
			</div>
			<el-input-number v-model="zoomValue" :min="20" :max="400" @change="onZoomValueChange">
				<template #decrease-icon>
					<el-icon><ZoomOut /></el-icon>
				</template>
				<template #increase-icon>
					<el-icon><ZoomIn /></el-icon>
				</template>
				<template #suffix>
					<span>%</span>
				</template>
			</el-input-number>
		</div>
	</div>
</template>

<script lang="ts" setup>
import { ref, watch, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { Close } from '@element-plus/icons-vue';
import {
	getErrorMessage,
	getKnowledgeGraphAPI,
	getKnowledgeRelationshipListAPI,
	getKnowledgeDetailAPI
} from '@/api/index';
import NetworkGraph from '@/components/graph/NetworkGraph.vue';
import TreeGraph from '@/components/graph/TreeGraph.vue';
import NodeInfoDialog from '@/components/common/NodeInfoDialog.vue';
import {
	NetworkNodeElType,
	MindMapNodeEvent,
	KnowledgeGraphNodeItemType,
	KnowledgeGraphLinkItemType,
	RelationStyleByType,
	KnowledgeRelationshipItemType,
	GetKnowledgeGraphResponseType,
	NodeItemType,
	LinkItemType,
	KnowledgeDetailItemType,
	SingleKnowledgeRelationItemType,
	SearchResultItemType
} from './KnowledgeGraphCom.type';

const router = useRouter();

const props = withDefaults(
	defineProps<{
		businessCode: string;
		rootName: string;
		type: 'lesson' | 'course';
	}>(),
	{
		businessCode: '',
		rootName: '',
		type: 'lesson'
	}
);

const emit = defineEmits<{
	(e: 'changeVideoTime', time: number): void; // 切换视频时间
}>();

watch(
	() => props.rootName,
	(newVal) => {
		if (newVal) {
			nextTick(async () => {
				await Promise.all([getKnowledgeRelationship(), getKnowledgeGraph()]);
				initData();
			});
		}
	},
	{
		immediate: true
	}
);

const activeTab = ref<'network' | 'tree'>('network');
const onTabItemClickHandle = (tab: 'tree' | 'network') => {
	activeTab.value = tab;
	searchNodeValue.value = '';
	showSearchResults.value = false;
	isVisibleNodeInfoDialog.value = false;
	zoomValue.value = 100;
	closeSearchResults();
};

const isVisibleNodeInfoDialog = ref(false);
const closeNodeInfoDialog = () => {
	isVisibleNodeInfoDialog.value = false;
};

// 知识点关系
const relationStyleByType = ref<RelationStyleByType>([]);
// 知识点关系Map
const relationMap = new Map<string, KnowledgeRelationshipItemType>();
// 节点类型映射（用于气泡框显示中文）
const typeMap = ref<Map<string, string>>(
	new Map([
		['knowledge', '知识点'],
		['course', '课程'],
		['lesson', '课堂']
	])
);
// 获取知识点关系
const getKnowledgeRelationship = async () => {
	try {
		const data = await getKnowledgeRelationshipListAPI({
			pageNum: 0,
			pageSize: 0
		});

		const _relationStyleByType: RelationStyleByType = [];
		data.list.forEach((item: KnowledgeRelationshipItemType) => {
			_relationStyleByType.push({
				label: item.relationName || '',
				isHierarchy: item.displayType === 'arrow'
			});
			if (item.relationCode) {
				relationMap.set(item.relationCode, item);
			}
		});

		relationStyleByType.value = _relationStyleByType;
	} catch (error) {
		getErrorMessage(error);
	}
};

// 知识图谱数据
const knowledgeGraphData = ref<GetKnowledgeGraphResponseType | null>(null);
// 知识点节点Map
const knowledgeNodeMap = new Map<string, KnowledgeGraphNodeItemType>();
// 获取知识图谱
const getKnowledgeGraph = async () => {
	try {
		let data = await getKnowledgeGraphAPI({
			businessCode: props.businessCode,
			businessType: props.type === 'course' ? 2 : 1
		});
		// 临时数据
		data = {
			links: [
				{
					id: 38,
					relationType: 'contains',
					source: 96030,
					target: 96031
				},
				{
					id: 39,
					relationType: 'belong',
					source: 96031,
					target: 96030
				},
				{
					id: 40,
					relationType: 'contains',
					source: 96030,
					target: 96032
				},
				{
					id: 41,
					relationType: 'belong',
					source: 96032,
					target: 96030
				},
				{
					id: 42,
					relationType: 'contains',
					source: 96030,
					target: 96033
				},
				{
					id: 43,
					relationType: 'belong',
					source: 96033,
					target: 96030
				},
				{
					id: 44,
					relationType: 'contains',
					source: 96030,
					target: 96034
				},
				{
					id: 45,
					relationType: 'belong',
					source: 96034,
					target: 96030
				},
				{
					id: 46,
					relationType: 'contains',
					source: 96030,
					target: 96035
				},
				{
					id: 47,
					relationType: 'belong',
					source: 96035,
					target: 96030
				},
				{
					id: 48,
					relationType: 'contains',
					source: 96030,
					target: 96036
				},
				{
					id: 49,
					relationType: 'belong',
					source: 96036,
					target: 96030
				}
			],
			nodes: [
				{
					id: 96030,
					name: '利润管理与分配',
					root: true
				},
				{
					id: 96031,
					name: '利润的概念与分类',
					root: false
				},
				{
					id: 96032,
					name: '利润的构成',
					root: false
				},
				{
					id: 96033,
					name: '利润的预测方法',
					root: false
				},
				{
					id: 96034,
					name: '利润分配的意义与原则',
					root: false
				},
				{
					id: 96035,
					name: '利润分配的基本程序',
					root: false
				},
				{
					id: 96036,
					name: '股利支付方式与日期',
					root: false
				}
			]
		};

		if (data.nodes?.length) {
			data.nodes.forEach((item: KnowledgeGraphNodeItemType) => {
				knowledgeNodeMap.set(String(item.id), item);
			});
		}

		knowledgeGraphData.value = data;
	} catch (error) {
		getErrorMessage(error);
	}
};

// 网络图数据
const networkGraphData = ref<{ nodes: NodeItemType[]; links: LinkItemType[] }>({
	nodes: [],
	links: []
});
// 知识关系数据
const links = ref<KnowledgeGraphLinkItemType[]>([]);
const containsLinks = ref<KnowledgeGraphLinkItemType[]>([]);
const linkMap = ref<Map<string, KnowledgeGraphLinkItemType[]>>(new Map());
// 树图数据
const treeData = ref<{ nodes: NodeItemType[]; links: LinkItemType[] }>({ nodes: [], links: [] });
// 初始化数据
const initData = () => {
	// 清空 Map，避免重复累积
	linkMap.value.clear();

	const _nodes: NodeItemType[] =
		knowledgeGraphData.value?.nodes.map((item: KnowledgeGraphNodeItemType) => ({
			id: String(item.id),
			label: item.name,
			type: 'knowledge',
			root: item.root
		})) || [];
	_nodes.unshift({
		id: 'root',
		label: props.rootName,
		type: props.type === 'course' ? 'course' : 'lesson'
	});

	links.value =
		knowledgeGraphData.value?.links.filter(
			(item: KnowledgeGraphLinkItemType) =>
				item.relationType !== 'belong' && item.relationType !== 'behind'
		) || [];
	containsLinks.value =
		knowledgeGraphData.value?.links.filter(
			(item: KnowledgeGraphLinkItemType) => item.relationType === 'contains'
		) || [];

	const _links =
		links.value.map((item: KnowledgeGraphLinkItemType) => {
			// 如果 Map 中不存在该关系类型，则创建新数组
			if (!linkMap.value.has(item.relationType)) {
				linkMap.value.set(item.relationType, []);
			}
			// 将当前项添加到对应的数组中
			linkMap.value.get(item.relationType)!.push(item);

			return {
				source: String(item.source),
				target: String(item.target),
				label: relationMap.get(item.relationType)?.relationName || '',
				isHierarchy: relationMap.get(item.relationType)?.displayType === 'arrow'
			};
		}) || [];
	// 树图仅需contains关系的连接
	const _containsLinks =
		containsLinks.value.map((item: KnowledgeGraphLinkItemType) => {
			return {
				source: String(item.source),
				target: String(item.target),
				label: relationMap.get('contains')?.relationName || '',
				isHierarchy: true
			};
		}) || [];

	// 增加links，找到root=true的节点，将root节点指向这些节点
	_nodes.forEach((node) => {
		if (node.id !== 'root' && node.root) {
			_links.push({
				source: 'root',
				target: node.id,
				label: relationMap.get('contains')?.relationName || '',
				isHierarchy: true
			});
			_containsLinks.push({
				source: 'root',
				target: node.id,
				label: relationMap.get('contains')?.relationName || '',
				isHierarchy: true
			});
		}
	});

	// 处理网络图数据
	networkGraphData.value = {
		nodes: _nodes,
		links: _links
	};

	// 处理树图数据
	treeData.value = {
		nodes: _nodes,
		links: _containsLinks
	};
};

/**
 * 知识图谱-网络图
 */
const networkGraphRef = ref<InstanceType<typeof NetworkGraph>>();

const nodeStyleByType = ref([
	{
		type: props.type === 'course' ? 'course' : 'lesson',
		color: '#1f77b4',
		size: 70,
		textPosition: 'center',
		backgroundImg: '/static/img/graph/lesson-node-bg.png' // 背景图（最高优先级）
	},
	{
		type: 'knowledge',
		color: '#d62728',
		size: 20,
		textPosition: 'bottom',
		// 渐变色（需要至少2个色标）
		// 径向渐变示例（从中心向外扩散）
		gradientType: 'radial',
		gradientStops: [
			{ offset: 0, color: '#94ABFF' }, // 中心颜色
			{ offset: 1, color: '#3C75F7' } // 边缘颜色
		]
	}
]);

const textName = ref('');
// 网络图节点被点击
const handleNetworkNodeClick = (node: NetworkNodeElType) => {
	console.log(node);
	if (node.id !== 'root') {
		textName.value = node.label || '';
		isVisibleNodeInfoDialog.value = true;
		getKnowledgeDetail(Number(node.id));
		getKnowledgeRelation(Number(node.id));
	} else {
		isVisibleNodeInfoDialog.value = false;
	}
};

/**
 * 知识图谱-树图
 */
const treeGraphRef = ref<InstanceType<typeof TreeGraph>>();

const themeConfig = ref({
	// 层级样式配置（colorMode="level" 时生效）
	root: {
		shape: 'roundedRectangle',
		fillColor: '#01B4FF',
		fontSize: 18
	},
	second: {
		shape: 'roundedRectangle',
		fillColor: '#3C75F7',
		fontSize: 14
	},
	node: {
		shape: 'roundedRectangle',
		fillColor: '#3C75F7',
		fontSize: 14
	}
});

/** -------------------------- 获取详情 -------------------------- */
// 树图节点被点击
const handleTreeNodeClick = (node: MindMapNodeEvent) => {
	console.log(node);
	if (node.nodeData?.data && node.nodeData.data.id !== 'root') {
		textName.value = (node.nodeData?.data?.text as string) || '';
		isVisibleNodeInfoDialog.value = true;
		getKnowledgeDetail(Number(node.nodeData?.data?.id));
		getKnowledgeRelation(Number(node.nodeData?.data?.id));
	} else {
		isVisibleNodeInfoDialog.value = false;
	}
};

// 获取知识点详情
const knowledgeDetail = ref<KnowledgeDetailItemType[]>([]);
const getKnowledgeDetail = async (knowledgeId: number) => {
	try {
		const data = await getKnowledgeDetailAPI({
			businessType: props.type === 'course' ? 2 : 1,
			knowledgeId
		});
		knowledgeDetail.value = data || [];
	} catch (error) {
		console.error(getErrorMessage(error));
		knowledgeDetail.value = [];
	}
};

// 整理出知识点关系
const knowledgeRelation = ref<SingleKnowledgeRelationItemType[]>([]);
const getKnowledgeRelation = (knowledgeId: number) => {
	const _knowledgeRelation: SingleKnowledgeRelationItemType[] = [];
	relationMap.forEach((item: KnowledgeRelationshipItemType) => {
		const _name = item.relationName;
		const _code = item.relationCode;
		// 有箭头的关系只取“出箭头”，无箭头的关系则直接取出
		const _link = knowledgeGraphData.value?.links
			?.filter(
				(link: KnowledgeGraphLinkItemType) =>
					_code &&
					link.relationType === _code &&
					((item.displayType === 'arrow' && link.source === knowledgeId) ||
						(item.displayType === 'none' &&
							(link.target === knowledgeId || link.source === knowledgeId)))
			)
			.map((link: KnowledgeGraphLinkItemType) => ({
				code: String(link.target),
				name: knowledgeNodeMap.get(String(link.target))?.name || ''
			}));
		if (_name && _link?.length) {
			_knowledgeRelation.push({
				name: _name,
				link: _link
			});
		}
	});
	knowledgeRelation.value = _knowledgeRelation;
};
// 切换视频时间
const onChangeVideoTime = (time: number) => {
	emit('changeVideoTime', time);
};

/** -------------------------- 搜索节点 -------------------------- */
// 搜索节点
const searchNodeValue = ref('');
const showSearchResults = ref(false);
// 搜索结果数据
const searchResults = ref<SearchResultItemType[]>([]);

const onSearchNodeHandle = (e: KeyboardEvent | MouseEvent) => {
	if (e.type === 'keydown' && (e as KeyboardEvent).key !== 'Enter') return;

	if (searchNodeValue.value) {
		showSearchResults.value = true;

		// 支持模糊匹配
		searchResults.value =
			knowledgeGraphData.value?.nodes
				.filter((node) => {
					if (!node.name) return false;
					// 检查节点标签是否包含任意一个搜索词
					return node.name.toLowerCase().includes(searchNodeValue.value.toLowerCase());
				})
				.map((node) => ({
					id: String(node.id),
					tag: '知识点',
					tagClass: 'tag-blue',
					title: node.name
				})) || [];

		if (activeTab.value === 'tree') {
			treeGraphRef.value?.highlightNodeByNodeName(searchNodeValue.value);
		} else if (activeTab.value === 'network') {
			networkGraphRef.value?.highlightNodesByLabelWithRelations(searchNodeValue.value);
		}
	} else {
		showSearchResults.value = false;
	}
};

// 关闭搜索结果
const closeSearchResults = () => {
	showSearchResults.value = false;
};

// 搜索结果项被点击
const onSearchResultItemClickHandle = (item: SearchResultItemType) => {
	if (activeTab.value === 'tree' && item.id) {
		treeGraphRef.value?.highlightNodeByNodeId(item.id);
	} else if (activeTab.value === 'network' && item.id) {
		networkGraphRef.value?.highlightNodes(item.id);
	}
};

/** -------------------------- 知识点关系管理 -------------------------- */
// 知识点关系全选
const onTypeAllClickHandle = () => {
	const _linkLabels = Array.from(linkMap.value.keys())
		.map((item) => relationMap.get(item)?.relationName || '')
		.filter((item) => item);
	onTypeSubItemClickHandle(_linkLabels);
};

// 知识点关系筛选
const onTypeSubItemClickHandle = (item: string | string[]) => {
	if (item && (typeof item === 'string' || Array.isArray(item))) {
		networkGraphRef.value?.highlightRelationsByLabel(item);
	}
};

// 进入知识关系管理
const onEnterManagementClickHandle = () => {
	sessionStorage.setItem(
		'knowledgeGraphReturnParams',
		JSON.stringify({
			businessCode: props.businessCode,
			businessType: props.type
		})
	);
	router.push({
		name: 'KnowledgeRelationManage',
		query: {
			from: 'knowledgeGraph'
		}
	});
};

/** -------------------------- 图谱工具栏 -------------------------- */
// 展开节点
const onExpandClickHandle = (level: string) => {
	if (activeTab.value === 'tree') {
		treeGraphRef.value?.showByLevel(level);
	} else if (activeTab.value === 'network') {
		networkGraphRef.value?.showByLevel(level);
	}
};

// 定位到中心
const onCenterClickHandle = () => {
	if (activeTab.value === 'tree') {
		treeGraphRef.value?.fitToCenter();
	} else if (activeTab.value === 'network') {
		networkGraphRef.value?.fitToCenter();
	}
};

// 缩放
const zoomValue = ref<number>(100);

// 缩放值变化处理
const onZoomValueChange = (value: number | undefined) => {
	if (value === undefined) return;

	// 限制范围
	const clampedValue = Math.max(20, Math.min(400, value));
	zoomValue.value = clampedValue;

	if (activeTab.value === 'network' && networkGraphRef.value) {
		networkGraphRef.value.setZoomValue(clampedValue);
	} else if (activeTab.value === 'tree' && treeGraphRef.value) {
		treeGraphRef.value.setZoomValue(clampedValue);
	}
};
</script>

<style lang="scss" scoped>
.knowledge-graph-wrapper {
	position: relative;
	padding-top: 0.7rem;
	width: 100%;
	height: 100%;

	&.course-graph {
		padding-top: 0;
	}

	.graph-search-wrapper {
		position: absolute;
		right: 1rem;
		top: 1.5rem;

		&.course-graph-search {
			right: 0;
			top: 0.9rem;
		}
	}

	.page-graph-wrapper {
		height: calc(100% - 5rem);
	}

	.type-group-wrapper {
		bottom: 0;
		left: 0;
		min-width: 20rem;

		&.lesson-graph-type-group {
			bottom: 1.6rem;
			left: 0.7rem;
		}

		.type-group-item-wrapper {
			max-height: 30rem;
		}
	}

	.lesson-graph-toolbar {
		bottom: 1.6rem;
		right: 1rem;
	}
}
</style>
